import os
import threading
import time
from importlib import reload

from django.conf import settings
from django.urls import clear_url_caches

import djmockserverproject.urls as djmockserver_urls
from djmockserver.mock.serializer import MockItem
from .common import getfile, get_mock

import logging

logger = logging.getLogger(__name__)


class MockLoader:
    """
    加载mock.json文件内容
    """
    _instance_lock = threading.Lock()

    def __new__(cls, *args, **kwargs):
        if not hasattr(MockLoader, "_instance"):
            with MockLoader._instance_lock:
                if not hasattr(MockLoader, "_instance"):
                    MockLoader._instance = object.__new__(cls)
        return MockLoader._instance

    def __init__(self, filepath: str):
        self._filepath = filepath
        self._data: dict = self._load()

    def _load(self):
        try:
            mock_dict = {}
            # 读取全部json文件
            content = get_mock(self._filepath)
            # {'uri':[],'uri2':[]}
            for item in content:
                m = MockItem(item)
                if m.uri in mock_dict:
                    mock_dict[m.uri].append(m)
                else:
                    mock_dict[m.uri] = [m]
            logger.info('mock file loaded data : {}'.format(mock_dict))
            return mock_dict
        except Exception as e:
            logger.error('load mock file exception : {}'.format(e))

    @property
    def data(self):
        return self._data


class MockListener:
    """
    检查mock.json文件修改时间
    """
    def __init__(self, filelist: list, mockloader: MockLoader):
        self._filelist = filelist
        self._mock_mtime_list = [os.path.getmtime(filepath) for filepath in filelist] # 原修改时间
        self._mockloader = mockloader

    def update(self):
        new_mtime_list = [os.path.getmtime(filepath) for filepath in self._filelist]
        for index, (new_mtime, old_mtime) in enumerate(zip(new_mtime_list, self._mock_mtime_list)):
            if old_mtime != new_mtime:
                self.log_mtime(new_mtime)
                self._mock_mtime_list[index] = new_mtime
                # 更新mockloader的数据
                logger.info('mock files had updated.')
                self._mockloader._data = self._mockloader._load()
                # 删除url缓存并更新
                clear_url_caches()
                reload(djmockserver_urls)
                logger.info('update uri ：{}'.format(djmockserver_urls.urlpatterns))
                # 只要一个文件更新,即全部更新
                break

    @staticmethod
    def log_mtime(mtime):
        logger.info('mock file modify time : {}'.format(
            time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(mtime))))


filelist = []
getfile(settings.MOCK_FILE_PATH, filelist)

mockloader = MockLoader(settings.MOCK_FILE_PATH)
mocklistener = MockListener(filelist, mockloader)


def watch_mockjson():
    logger.info('watching mock files')
    while True:
        try:
            time.sleep(settings.REFRESH_INTERVAL)
            mocklistener.update()
        except Exception as e:
            logger.info('mock file update exception : {}'.format(e))

# 监听文件更新的线程
try:
    t = threading.Thread(target=watch_mockjson)
    # 必须设置为守护线程，否则无法停止
    t.setDaemon(True)
    t.start()
except Exception as e:
    logger.info('thread watching mock files raise exception : {}'.format(e))
